
module top(

    input rst_n, // system reset, active low
    input [3:0] key, // key button input
    input  touch_key,

    // vga interface
    input vga_clk,
    output [15:0] vga_rgb, // with r in MSB
    output vga_hsync,
    output vga_vsync,
    output vga_de
    );

// ************ parameter define ***********

//game status
parameter LAUNCHING=2'b00;
parameter PLAYING=2'b01;
parameter DIE_FLASHING=2'b10;
parameter INITIALIZING=2'b11;


// parameter BLOCK_X_MAX = 7'd49;
// parameter BLOCK_Y_MAX = 7'd29;
parameter BLOCK_X_MAX = 7'd79;
parameter BLOCK_Y_MAX = 7'd59;

// control button
parameter UP=2'b00;
parameter RIGHT=2'b01;
parameter DOWN=2'b10;
parameter LEFT=2'b11;

// the maximum length of the snake
parameter SNAKE_MAX_LEN = 32;


// ************ reset and clk ****************
wire pixel_clk; // vga driver clk, 25.2M

assign pixel_clk = vga_clk;



// ************ game status signal ****************

//00: LAUNCHING  01: PLAYING   10:DIE_FLASHING   11: INITIALIZING
wire [1:0] game_status;
wire [1:0] game_level;

wire [6:0] apple_x; // apple x index
wire [6:0] apple_y; // apple y index

wire [SNAKE_MAX_LEN*7-1:0] snake_x_bus; // snake x index bus
wire [SNAKE_MAX_LEN*7-1:0] snake_y_bus; // snake y index bus

wire [SNAKE_MAX_LEN-1:0] snake_piece_is_display;  // indicate whether to display each pieces

wire get_apple; // the snake has eaten an apple

// direction  00: UP   01: Right   10: DOWN   11: LEFT
wire [1:0] current_direction;

wire hit_wall;
wire hit_itself;


// ************ time and score  ****************

wire [7:0] score;
wire [11:0] run_time;

// ************ snake control signal ****************

wire [3:0] key_filter; // button after deboundce

wire turn_left;
wire turn_right;
wire turn_up;
wire turn_down;

//reg define
reg touch_key_d0;
reg touch_key_d1;
wire touch_en;
reg game_pause;


// ************ key debounce  ****************
always @ (posedge pixel_clk or negedge rst_n) begin
    if(!rst_n) begin
        game_pause<=1'b0;
    end
    else if(game_status != PLAYING)begin
        game_pause<=1'b0;
    end
    else if(touch_en)begin
        game_pause<=~game_pause;
    end
end

ax_debounce#(
    .FREQ(150)
)  ax_debounce_0(
	.clk        (pixel_clk),
	.rst         (~rst_n),
	.button_in        (key[0]),      
	.button_negedge   (key_filter[0])
);


ax_debounce#(
    .FREQ(150)
)  ax_debounce_1(
	.clk        (pixel_clk),
	.rst         (~rst_n),
	.button_in        (key[1]),     
	.button_negedge   (key_filter[1])   
);
ax_debounce#(
    .FREQ(150)
)  ax_debounce_2(
	.clk        (pixel_clk),
	.rst         (~rst_n),
	.button_in        (key[2]),      
	.button_negedge   (key_filter[2])   
);
ax_debounce#(
    .FREQ(150)
)  ax_debounce_3(
	.clk        (pixel_clk),
	.rst         (~rst_n),
	.button_in        (key[3]), 
	.button_negedge   (key_filter[3])  
);

ax_debounce#(
    .FREQ(150)
)  ax_debounce_4(
	.clk        (pixel_clk),
	.rst         (~rst_n),
	.button_in        (touch_key), 
	.button_negedge   (touch_en)  
);
// ************ display control  ****************
display#(
    .LAUNCHING(LAUNCHING),
    .PLAYING(PLAYING),
    .DIE_FLASHING(DIE_FLASHING),
    .INITIALIZING(INITIALIZING),
    .BLOCK_X_MAX(BLOCK_X_MAX),
    .BLOCK_Y_MAX(BLOCK_Y_MAX),
    .UP(UP),
    .RIGHT(RIGHT),
    .DOWN(DOWN),
    .LEFT(LEFT),
    .SNAKE_MAX_LEN(SNAKE_MAX_LEN)
)  u_display
(
    // vga signal
	.clk           (pixel_clk),
    .rst_n          (rst_n),
	.vga_hsync         (vga_hsync),
	.vga_vsync         (vga_vsync),
    .vga_de        (vga_de),
    .vga_rgb       (vga_rgb),
    // game signal
    .game_status    (game_status),
    .game_level(game_level),
    .score(score),
    .run_time(run_time),
    .apple_x(apple_x),
    .apple_y(apple_y),
    .snake_x_bus(snake_x_bus),
    .snake_y_bus(snake_y_bus),
    .snake_piece_is_display(snake_piece_is_display),
    .current_direction(current_direction)
);

// ************ generate apple  ****************



apple#(
    .LAUNCHING(LAUNCHING),
    .PLAYING(PLAYING),
    .DIE_FLASHING(DIE_FLASHING),
    .INITIALIZING(INITIALIZING),
    .BLOCK_X_MAX(BLOCK_X_MAX),
    .BLOCK_Y_MAX(BLOCK_Y_MAX)
) u_apple 
(
    .clk(pixel_clk),
    .apple_x(apple_x),
    .apple_y(apple_y),
    .get_apple(get_apple),
    .game_level(game_level),
    .game_pause(game_pause),
    .game_status(game_status)
);

// ************ control  the movement of the snake  ****************

assign turn_up = key_filter[0];
assign turn_down = key_filter[1];
assign turn_left = key_filter[2];
assign turn_right = key_filter[3];

turn#(
    .LAUNCHING(LAUNCHING),
    .PLAYING(PLAYING),
    .DIE_FLASHING(DIE_FLASHING),
    .INITIALIZING(INITIALIZING),
    .UP(UP),
    .RIGHT(RIGHT),
    .DOWN(DOWN),
    .LEFT(LEFT)
) u_turn 
(
    .clk(pixel_clk),
    .game_pause(game_pause),
    .turn_up(turn_up),
    .turn_down(turn_down),
    .turn_left(turn_left),
    .turn_right(turn_right),
    .game_status    (game_status),
    .current_direction(current_direction)
);

// ************ control  ganme status  ****************

fsm#(
    .LAUNCHING(LAUNCHING),
    .PLAYING(PLAYING),
    .DIE_FLASHING(DIE_FLASHING),
    .INITIALIZING(INITIALIZING)
)  u_fsm
(

	.clk(pixel_clk),
    .rst_n(rst_n),

    .game_status(game_status),
    .game_level(game_level),
    .hit_wall(hit_wall),
    .hit_itself(hit_itself),

    .turn_up(turn_up),
    .turn_down(turn_down),
    .turn_left(turn_left),
    .turn_right(turn_right)


);
// ************ snake  ****************

snake#(
    .LAUNCHING(LAUNCHING),
    .PLAYING(PLAYING),
    .DIE_FLASHING(DIE_FLASHING),
    .INITIALIZING(INITIALIZING),
    .BLOCK_X_MAX(BLOCK_X_MAX),
    .BLOCK_Y_MAX(BLOCK_Y_MAX),
    .UP(UP),
    .RIGHT(RIGHT),
    .DOWN(DOWN),
    .LEFT(LEFT),
    .SNAKE_MAX_LEN(SNAKE_MAX_LEN)
) u_snake(
    .clk(pixel_clk),
    
    .game_pause(game_pause),
    .game_status(game_status),
    .game_level(game_level),

    .current_direction(current_direction),

    .apple_x(apple_x),
    .apple_y(apple_y),

    .snake_x_bus(snake_x_bus),
    .snake_y_bus(snake_y_bus),

    .snake_piece_is_display(snake_piece_is_display),
    .get_apple(get_apple),
    .hit_wall(hit_wall),
    .hit_itself(hit_itself)
);


// ************ time and score  ****************

reg [3:0] score_l;
reg [3:0] score_h;

reg get_apple_r;

assign score = {score_h,score_l};

always@(posedge pixel_clk or negedge rst_n)begin
    if(~rst_n)begin
        get_apple_r<='b0;
    end
    else begin
        get_apple_r<=get_apple;
    end
end

always@(posedge pixel_clk or negedge rst_n)begin
    if(~rst_n)begin
        score_l<='b0;
    end
    else if(game_status ==INITIALIZING && (turn_down || turn_up || turn_left || turn_right))begin
        score_l<='b0;
    end
    else if(~game_pause && get_apple && ~get_apple_r)begin
        if(score_l<4'd9)
            score_l<=score_l+1;
        else
            score_l<='b0;
    end
end

always@(posedge pixel_clk or negedge rst_n)begin
    if(~rst_n)begin
        score_h<='b0;
    end
    else if(game_status ==INITIALIZING && (turn_down || turn_up || turn_left || turn_right))begin
        score_h<='b0;
    end
    else if(~game_pause && get_apple && ~get_apple_r && score_l == 4'd9)begin
        if(score_h<4'd9)
            score_h<=score_h+1;
        else
            score_h<='b0;
    end
end


reg [3:0] time_l;
reg [3:0] time_m;
reg [3:0] time_h;

reg [24:0] clk_cnt;

wire cnt_en;

assign cnt_en = clk_cnt == 'd25_199_999;

always@(posedge pixel_clk or negedge rst_n)begin
    if(~rst_n)begin
        clk_cnt<='b0;
    end
    else if(game_status!=PLAYING)
        clk_cnt<='b0;
    else if(clk_cnt<'d25_199_999 && ~game_pause)begin
        clk_cnt<=clk_cnt+1;
    end
    else begin
        clk_cnt<='b0;
    end
end

always@(posedge pixel_clk or negedge rst_n)begin
    if(~rst_n)begin
        time_l<='b0;
    end
    else if(game_status ==INITIALIZING && (turn_down || turn_up || turn_left || turn_right))begin
        time_l<='b0;
    end
    else if(~game_pause && cnt_en)begin
        if(time_l<4'd9)
            time_l<=time_l+1;
        else
            time_l<='b0;
    end
end

always@(posedge pixel_clk or negedge rst_n)begin
    if(~rst_n)begin
        time_m<='b0;
    end
    else if(game_status ==INITIALIZING && (turn_down || turn_up || turn_left || turn_right))begin
        time_m<='b0;
    end
    else if(~game_pause && cnt_en && time_l == 4'd9)begin
        if(time_m<4'd9)
            time_m<=time_m+1;
        else
            time_m<='b0;
    end
end

always@(posedge pixel_clk or negedge rst_n)begin
    if(~rst_n)begin
        time_h<='b0;
    end
    else if(game_status ==INITIALIZING && (turn_down || turn_up || turn_left || turn_right))begin
        time_h<='b0;
    end
    else if(~game_pause && cnt_en && time_l == 4'd9 && time_m == 4'd9)begin
        if(time_h<4'd9)
            time_h<=time_h+1;
        else
            time_h<='b0;
    end
end

assign run_time = {time_h,time_m,time_l};


endmodule
